package sharefolder

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/shirou/gopsutil/v3/disk"
	"gitlab.local/TerraMaster/tos-modules/service/sharefolder"
	"io/fs"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"unicode"

	"github.com/gogf/gf/errors/gerror"
	"github.com/gogf/gf/os/glog"
	"gitlab.local/TerraMaster/tos-modules/core/acl"
	"gitlab.local/TerraMaster/tos-modules/core/tosutils"
	"gitlab.local/TerraMaster/tos-modules/database/sqlitemodel"
	"gitlab.local/TerraMaster/tos-modules/directory"
	"gitlab.local/TerraMaster/tos-modules/errcode"
	"gitlab.local/golibrary/apputils"
	"gitlab.local/golibrary/linux"
	"gitlab.local/golibrary/openssl"
	"gitlab.local/golibrary/user"
	"gitlab.local/golibrary/utils"
)

func (f *Folder) GetFolderFromDb(name string) *directory.CreateFolder {
	db := f.dbService.DbGet()
	result := directory.CreateFolder{}
	err := db.Table("share").Where("foldername=?", name).Find(&result.Share).Error
	if err != nil {
		return nil
	}
	if !utils.Exists(result.MntPath) {
		//实际目录不存在就清除数据库中的记录...
		db.Table("share").Where("folder_id=?", result.FolderId).Delete(&sqlitemodel.Share{})
		return nil
	}
	return &result
}

func (f *Folder) Acl() acl.FolderAttrService {
	return f.aclService
}

func (f *Folder) IsExistsFolder(name string) bool {
	share := f.GetFolderFromDb(name)
	if share != nil {
		return true
	}
	//同时检查用户中是否被使用
	_, e := user.Lookup(name)
	return e == nil
}

func (f *Folder) IsExistsFolderCaseInsensitive(name string) bool {
	share := f.GetFolderFromDb(name)
	if share != nil {
		return true
	}
	var exists int64
	db := f.dbService.DbGet().Table("share")
	_ = db.Where("lower(foldername)=lower(?)", name).Count(&exists)
	if exists > 0 {
		return true // exist
	}
	//同时检查用户中是否被使用
	//_, e := user.Lookup(name)
	//屏蔽掉大小写
	var userCount int64
	db1 := f.dbService.DbGet().Table("user_table")
	_ = db1.Where("lower(username)=lower(?)", name).
		Count(&userCount)
	if userCount > 0 {
		return true // exist
	}
	return false
}

func (f *Folder) GetFolderList(name string) []sharefolder.ShareFolder {
	result := make([]sharefolder.ShareFolder, 0)
	var public []sharefolder.ShareFolder
	// 1、优化获取文件夹所属的卷是否挂载的判断,当有smb挂载时候cpu飙升
	df, _ := disk.Partitions(true)
	mp := make(map[string]struct{}, len(df))
	for _, v := range df {
		mp[v.Mountpoint] = struct{}{}
	}

	volumeList := f.diskService.AllVolumeList()
	var vData = make(map[string]string)
	for _, v := range volumeList {
		vData[strings.TrimPrefix(v.MntPath, "/")] = v.FileSystem
	}

	for _, item := range f.dirService.Get() {
		dirPath := item.MntPath
		if item.Ecryptfs == 1 {
			dirPath = filepath.Join("/", item.Device, fmt.Sprintf("@%s@", item.FolderName))
		}
		volume := filepath.Dir(item.MntPath)

		_, volumeMounted := mp[volume]
		_, pathMounted := mp[item.MntPath]
		mounted := volumeMounted || pathMounted
		//if !utils.Exists(dirPath) || f.getMountPointByPath(item.MntPath) == "" || strings.HasPrefix(item.Device, "@") {
		if !utils.Exists(dirPath) || !mounted || strings.HasPrefix(item.Device, "@") {
			continue
		}
		disabled := false
		//if item.FolderName == "public" || f.isMounted(item.MntPath) {
		if item.FolderName == "public" || pathMounted {
			disabled = true
		}
		info, err := utils.CustomStat(dirPath)
		if err != nil {
			_, _ = fmt.Fprintln(os.Stderr, "[folder.go]:"+err.Error())
		}
		createTime := info.ChangeTime() // Stat_t.Ctime = create time
		tmpPermission, _ := f.aclService.UserAcl(item.MntPath, name)
		sf := sharefolder.ShareFolder{
			Share:      item,
			VolumeName: regexp.MustCompile(`Volume\d+`).FindString(item.MntPath),
			Disabled:   disabled,
			CreateTime: createTime.String(),
			RealAcl:    tmpPermission,
		}
		if item.Ecryptfs == 1 { //判断加密文件夹是否被挂载
			//if linux.GetDeviceInfoByPath(item.MntPath) != nil {
			//	sf.IsLoad = true
			//}
			dir, base := filepath.Split(item.MntPath)
			ecryptfsPath := filepath.Join(dir, strings.Trim(base, "@"))
			if _, ok := mp[ecryptfsPath]; ok {
				sf.IsLoad = true
			}

		}

		sf.FileSystem = vData[sf.VolumeName]

		if sf.FolderName == "public" {
			public = []sharefolder.ShareFolder{sf}
		} else {
			result = append(result, sf)
		}
	}
	res := append(public, result...)
	sort.SliceStable(res, func(i, j int) bool {
		return res[i].FolderId < res[j].FolderId
	})
	return res
}

// isXfsVolume 判断路径是否为xfs卷
func (f *Folder) isXfsVolume(vol string) bool {
	var df *disk.PartitionStat
	dfs, err := disk.Partitions(false)
	if err != nil {
		return false
	}
	for _, item := range dfs {
		if vol == item.Mountpoint {
			df = &item
			break
		}
	}
	if df == nil {
		return false
	}
	return df.Fstype == "xfs"
}

func (f *Folder) GetFolderInfo(id int) (*sharefolder.Detail, error) {
	folder := f.dirService.GetFolder(id)
	volume := filepath.Dir(folder.MntPath)
	tail := linux.GetDeviceInfoByPath(volume)
	if tail == nil {
		return nil, errors.New("invalid folder id")
	}
	result := sharefolder.Detail{ModifyFolder: directory.ModifyFolder{Share: folder}, FileSystem: tail.Filesystem, IsHyperLockFile: f.isXfsVolume(volume)}
	info, _ := linux.GetSizeByPath(folder.MntPath)
	result.VolumeSize = info["Total"] / 1024 / 1024
	folderAttrs, err := f.aclService.GetAttrsByID(folder.FolderId)
	if err != nil {
		return nil, gerror.Wrap(err, "GetFolderInfo")
	}
	fname, _ := f.aclService.GetAclTypeByFile(folder.FolderName)
	groupMapIdx := make(map[string]int, 0)
	//遍历所有用户组
	groupList, _ := f.userService.GroupList(0, 0)
	for idx, groupItem := range groupList {
		groupMapIdx[groupItem.Name] = idx
		if aclType, ok := fname["@"+groupItem.Name]; ok && aclType == int(acl.RichAcl) {
			aclItem := acl.Attrs{}
			aclItem.Type = "group"
			aclItem.AclType = aclType
			aclItem.Name = groupItem.Name
			result.GroupsAcl = append(result.GroupsAcl, aclItem)
			continue
		}

		aclItem := acl.Attrs{
			Type: "group",
			Name: groupItem.Name,
		}
		aclItem.Permission, aclItem.Effective = f.aclService.QueryAclByUser(folderAttrs.Acl, groupItem.Name, "group")

		if aclItem.Effective == "" {
			aclItem.Permission = "---"
			aclItem.Effective = "---"
		}
		result.GroupsAcl = append(result.GroupsAcl, aclItem)
	}

	//遍历所有用户
	userList, _ := f.userService.UserList(0, 0)
	for _, userItem := range userList {
		aclItem := sharefolder.UserAttrs{}
		// 比较用户组的权限，自定义 > 禁止 > 读写 > 只读
		maxAcl := ""
		maxAclType := int(acl.PosixAcl)
		for _, groupName := range userItem.GroupList {
			if idx, ok := groupMapIdx[groupName]; ok && maxAcl != "define" {
				maxAcl, maxAclType = acl.AclRight(result.GroupsAcl[idx].Permission).Gte(maxAcl, acl.AclType(result.GroupsAcl[idx].AclType), acl.AclType(maxAclType))
				//if acl.AclRight(result.GroupsAcl[idx].Permission).Gte(maxAcl) {
				//	maxAcl = result.GroupsAcl[idx].Permission
				//}

				//if result.GroupsAcl[idx].AclType == int(acl.RichAcl) {
				//	maxAclType = acl.RichAcl
				//	break
				//}
			}
		}
		aclItem.GroupAcl = maxAcl

		if aclType, ok := fname[userItem.Name]; ok && aclType == int(acl.RichAcl) {
			aclItem.Type = "user"
			aclItem.AclType = aclType
			aclItem.Name = userItem.Name
			aclItem.Groups = userItem.GroupList
			result.UsersAcl = append(result.UsersAcl, aclItem)
			continue
		}

		aclItem.Type = "user"
		aclItem.Name = userItem.Name
		aclItem.Permission, aclItem.Effective = f.aclService.QueryAclByUser(folderAttrs.Acl, userItem.Name, "user")
		aclItem.Groups = userItem.GroupList
		aclItem.IsFirstAdmin = os.Getenv("admin") == userItem.Name
		result.UsersAcl = append(result.UsersAcl, aclItem)
	}
	//遍历所有系统用户
	sysUserList, _ := f.userService.SysUserList(0, 0)
	for _, sysItem := range sysUserList {
		if aclType, ok := fname[sysItem.Name]; ok && aclType == int(acl.RichAcl) {
			aclItem := sharefolder.UserAttrs{}
			aclItem.Type = "user"
			aclItem.AclType = aclType
			aclItem.Name = sysItem.Name
			result.UsersAcl = append(result.UsersAcl, aclItem)
			continue
		}

		aclItem := sharefolder.UserAttrs{}
		aclItem.Type = "user"
		aclItem.Name = sysItem.Name
		aclItem.Permission, aclItem.Effective = f.aclService.QueryAclByUser(folderAttrs.Acl, sysItem.Name, "user")
		aclItem.Groups = sysItem.GroupList
		aclItem.IsFirstAdmin = os.Getenv("admin") == sysItem.Name

		if aclItem.Effective == "" {
			aclItem.Permission = "---"
			aclItem.Effective = "---"
		}
		result.SysUserAcl = append(result.SysUserAcl, aclItem)
	}
	result.WebDav = make([]sharefolder.WebDavAttr, 0)
	davAcl := f.GetWebDavAcl(id)
	for _, davItem := range davAcl {
		davAttr := sharefolder.WebDavAttr{UserName: davItem.Username, Permission: davItem.Access}
		result.WebDav = append(result.WebDav, davAttr)
	}
	return &result, nil
}

// CreateFolder 创建共享文件夹
func (f *Folder) CreateFolder(user string, option *directory.CreateFolder) error {
	if option.PassPhrase != "" {
		option.PassPhrase, _ = openssl.JsEcryptDecode(option.PassPhrase)
		//option.PassPhrase = f.EscapePassword(option.PassPhrase)
	}
	if f.IsExistsFolder(option.FolderName) {
		return FileExists
	}
	if len(option.Acls) >= 1 {
		if option.Acls[0].Type == "user" {
			_ = f.addUserGroupInformation(option)
		}
	}
	mntPath, err := f.dirService.Create(user, *option)
	if err != nil {
		return gerror.Wrap(err, "CreateFolder")
	}
	_ = f.aclService.SetByPath(mntPath, false, option.Acls)
	dir, base := filepath.Split(mntPath)
	mountPoint := filepath.Join(dir, strings.Trim(base, "@")) //去除首尾多余@符号
	if option.Ecryptfs == 1 && len(option.PassPhrase) >= 8 {
		if err = f.dirService.EcryptfsMount(mntPath, option.PassPhrase); err != nil {
			return gerror.Wrap(err, "CreateFolder1")
		}
		_ = f.saveSecretKey(mountPoint)
	}
	if err = f.addDefaultNfsAcl(mountPoint); err != nil {
		return gerror.Wrap(err, "CreateFolder2")
	}
	if err = f.addDefaultSmbAcl(mountPoint); err != nil {
		return gerror.Wrap(err, "CreateFolder3")
	}
	if err = f.addDefaultWebdavAcl(mountPoint); err != nil {
		return gerror.Wrap(err, "CreateFolder4")
	}
	tosutils.ReStartBaseService("")
	// _ = mediaindex.AddFolderToMediaIndex(mountPoint)
	return err
}

// restoreSecretKey 还原秘钥
func (f *Folder) restoreSecretKey() error {
	res, _ := utils.ShellExec(`df-json | grep -E "/Volume[0-9]+$" | awk '{print $8}'`)
	res = strings.TrimSpace(res)
	if res == "" {
		return nil
	}
	db := f.dbService.DbGet()
	for _, v := range strings.Split(res, "\n") {
		src := filepath.Join("/", v, "@cache", ".secretKey")
		if !utils.Exists(src) {
			continue
		}
		file, err := os.ReadFile(src)
		if err != nil {
			continue
		}
		secretKey := make(SecretKey)
		if err = json.Unmarshal(file, &secretKey); err != nil {
			continue
		}
		for key, val := range secretKey {
			var data sqlitemodel.Share
			_ = db.Where("ecryptfs=1 and foldername=?", key).Select("folder_id").First(&data)
			if data.FolderId == 0 {
				continue
			}
			var cryptData sqlitemodel.ShareCrypt
			_ = db.Where("folder_id=?", data.FolderId).Select("passphrase").First(&cryptData)
			if cryptData.Passphrase != "" {
				continue
			}
			_ = db.Create(&sqlitemodel.ShareCrypt{
				FolderID:    data.FolderId,
				Passphrase:  val.Passphrase,
				Passtip:     val.Passtip,
				EcryptfsSig: val.EcryptfsSig,
			})
		}
	}
	return nil
}

// deleteSecretKey 删除秘钥
func (f *Folder) deleteSecretKey(id int) error {
	var data sqlitemodel.Share
	_ = f.dbService.DbGet().Where("ecryptfs=1 and folder_id=?", id).Select("foldername", "device").First(&data)
	if data.FolderName == "" {
		return nil
	}
	src := filepath.Join("/", data.Device, "@cache", ".secretKey")
	if !utils.Exists(src) {
		return nil
	}
	file, err := os.ReadFile(src)
	if err != nil {
		return gerror.Wrap(err, "deleteSecretKey")
	}
	secretKey := make(SecretKey)
	if err = json.Unmarshal(file, &secretKey); err != nil {
		return gerror.Wrap(err, "deleteSecretKey1")
	}
	delete(secretKey, data.FolderName)
	marshal, err := json.Marshal(secretKey)
	if err != nil {
		return gerror.Wrap(err, "deleteSecretKey2")
	}
	return os.WriteFile(src, marshal, os.ModePerm)
}

// saveSecretKey 保存秘钥
func (f *Folder) saveSecretKey(path string) error {
	var data sqlitemodel.Share
	db := f.dbService.DbGet()
	_ = db.Where("ecryptfs=1 and mntpath=?", path).Select("folder_id", "device", "foldername").First(&data)
	if data.FolderId == 0 {
		return nil
	}
	var res sqlitemodel.ShareCrypt
	_ = db.Where("folder_id=?", data.FolderId).First(&res)
	if res.Passphrase == "" {
		return nil
	}
	secretKey := make(SecretKey)
	src := filepath.Join("/", data.Device, "@cache", ".secretKey")
	if utils.Exists(src) {
		file, err := os.ReadFile(src)
		if err != nil {
			return gerror.Wrap(err, "saveSecretKey")
		}
		if err = json.Unmarshal(file, &secretKey); err != nil {
			return gerror.Wrap(err, "saveSecretKey1")
		}
	}
	secretKey[data.FolderName] = SecretKeyInfo{
		Passphrase:  res.Passphrase,
		Passtip:     res.Passtip,
		EcryptfsSig: res.EcryptfsSig,
	}
	marshal, err := json.Marshal(secretKey)
	if err != nil {
		return gerror.Wrap(err, "saveSecretKey2")
	}
	return os.WriteFile(src, marshal, os.ModePerm)
}

// 整合用户组信息
func (f *Folder) addUserGroupInformation(options *directory.CreateFolder) error {
	groupList, err := f.userService.GroupList(0, 0)
	if err != nil {
		return gerror.Wrap(err, "Failed to get user group information")
	}
	for _, group := range groupList {
		if group.Name == "admin" {
			options.Acls = append(options.Acls, acl.Attrs{Type: "group", Name: group.Name, Permission: "rwx"})
		} else {
			options.Acls = append(options.Acls, acl.Attrs{Type: "group", Name: group.Name, Permission: "---"})
		}

	}
	return nil
}

// addDefaultNfsAcl 添加一条nfs默认规则
func (f *Folder) addDefaultNfsAcl(path string) error {
	var data sqlitemodel.Share
	_ = f.dbService.DbGet().Select("folder_id").Where("mntpath=?", path).First(&data)
	if data.FolderId == 0 {
		return errors.New("data does not exist")
	}
	return f.SetNFSAcl(sharefolder.NFSAcl{
		FolderId:       data.FolderId,
		Host:           "*",
		Permission:     2,
		Squash:         0,
		Security:       "sys",
		Async:          true,
		IsMount:        true,
		PrivilegedPort: false,
	})
}

// addDefaultSmbAcl 添加一条SmbAcl默认规则
func (f *Folder) addDefaultSmbAcl(mntPath string) error {
	var data sqlitemodel.Share
	_ = f.dbService.DbGet().Select("folder_id").Where("mntpath=?", mntPath).First(&data)
	if data.FolderId == 0 {
		return errors.New("data does not exist")
	}
	return f.dbService.DbGet().Create(&sqlitemodel.AclHost{
		Host:     "*",
		FolderId: data.FolderId,
		Service:  "samba",
		Access:   1,
		Hash:     utils.Gethashstring(fmt.Sprintf("%s%d%s", "samba", data.FolderId, "*")),
	}).Error
}

// addDefaultWebdavAcl 添加一条 WebDAV 默认规则
func (f *Folder) addDefaultWebdavAcl(mntPath string) error {
	var data sqlitemodel.Share
	_ = f.dbService.DbGet().Select("folder_id").Where("mntpath=?", mntPath).First(&data)
	if data.FolderId == 0 {
		return errors.New("data does not exist")
	}
	return f.dbService.DbGet().Create(&sqlitemodel.AclHost{
		Host:     "*",
		FolderId: data.FolderId,
		Service:  "webdav",
		Access:   1,
		Hash:     utils.Gethashstring(fmt.Sprintf("%s%d%s", "webdav", data.FolderId, "*")),
	}).Error
}

func (f *Folder) EditFolder(id int, folder sharefolder.Detail) error {
	mntPath, err := f.dirService.Set(id, folder.ModifyFolder)
	if err != nil {
		return gerror.Wrap(err, "EditFolder")
	}
	dir, base := filepath.Split(mntPath)
	mntPathRecycle := filepath.Join(dir, strings.Trim(base, "@"))
	//设置回收站状态
	_ = f.recycleStatus(mntPathRecycle, folder.Recycle)

	go tosutils.ReStartBaseService("")
	return nil
}

func (f *Folder) EditFolderPermission(id int, folder sharefolder.Detail) error {
	mntPath := f.dirService.GetFolder(id).MntPath
	var attrs []acl.Attrs
	for _, item := range folder.UsersAcl {
		attrs = append(attrs, item.Attrs)
	}
	for _, item := range folder.SysUserAcl {
		attrs = append(attrs, item.Attrs)
	}
	//设置权限
	if err := f.aclService.SetByPath(mntPath, folder.Replace, append(attrs, folder.GroupsAcl...)); err != nil {
		return gerror.Wrap(err, "EditFolder1")
	}

	go tosutils.ReStartBaseService("")
	return nil
}

// 通过文件路径设置文件的acl
func (f *Folder) EditFolderRichAcl(path string, attrs acl.FolderAttrs) error {
	if err := f.aclService.SetByPath(path, true, attrs.Acl); err != nil {
		return gerror.Wrap(err, "EditFolderAcl")
	}

	//go tosutils.ReStartBaseService("")
	return nil
}

// GetFolderRichAclInfo 获取文件中所有的Acl权限
func (f *Folder) GetFolderRichAclInfo(path string) (*acl.FolderAttrs, error) {
	//设置成richAcl模式
	old := f.aclService.SetNextOptAclType(acl.RichAcl)
	//改回之前的模式
	defer f.aclService.SetNextOptAclType(old)

	//获取当前目录的所有Acl权限。
	fattrs, err := f.aclService.GetAllAclByPath(path)

	return fattrs, err
}

// SetRecycleStatus 设置回收站状态 b 0 取消 2 勾选
func (f *Folder) SetRecycleStatus(path string, b int) error {
	return f.recycleStatus(path, b)
}

// 隐藏和显示回收站
// b false删除 true添加
func (f *Folder) recycleStatus(path string, b int) error {
	path = filepath.Join(path, "#recycle")
	if b == 0 {
		return os.RemoveAll(path)
	}

	if !utils.Exists(path) {
		if err := os.Mkdir(path, os.ModePerm); err != nil {
			return err
		}
		if err := os.Chmod(path, os.ModePerm); err != nil {
			return err
		}
		if _, err := utils.ShellExec(fmt.Sprintf(`setfacl -b '%s'`, path)); err != nil {
			return gerror.Wrap(err, "recycleStatus")
		}

		if _, err := utils.ShellExec(fmt.Sprintf(`setfacl -m g:allusers:rwx '%s'`, path)); err != nil {
			return gerror.Wrap(err, "recycleStatus")
		}
		return nil
	}

	//设置只有管理员可以查看回收站的内容
	err := f.EditAcl(b, path)
	if err != nil {
		return gerror.Wrap(err, "recycleStatus err")
	}

	return nil
}

func (f *Folder) DeleteFolder(id int) error {
	if f.snapshotOccupation(id) {
		return gerror.NewCode(errcode.OccupiedBySnapshots)
	}
	_ = f.deleteSecretKey(id)
	err := f.dirService.Del(id)
	_ = f.clearNfsAndSmbAcl(id)
	tosutils.ReStartBaseService("")
	return err
}

// snapshotOccupation 查询快照占用
func (f *Folder) snapshotOccupation(id int) bool {
	state := apputils.CheckAppState(snapshotName)
	if state == nil {
		return false
	}
	path := filepath.Join(state.InstallPath, "Snapshot", "cronTable", "duplicate")
	if !utils.Exists(path) {
		return false
	}

	var data sqlitemodel.Share
	_ = f.dbService.DbGet().Where("folder_id=?", id).First(&data)
	if data.MntPath == "" {
		return false
	}

	dir, err := os.ReadDir(path)
	if err != nil {
		glog.Warning("snapshotOccupation", err)
		return false
	}
	for _, v := range dir {
		file, err := os.ReadFile(filepath.Join(path, v.Name()))
		if err != nil {
			glog.Warning("snapshotOccupation1", err)
			continue
		}
		var snapshotConf SnapshotConf
		if err = json.Unmarshal(file, &snapshotConf); err != nil {
			glog.Warning("snapshotOccupation2", err)
			continue
		}
		dirPath := ""
		switch snapshotConf.Type {
		case "local":
			dirPath = filepath.Join("/", snapshotConf.Volume, snapshotConf.DistDirName)
		case "remote":
			if snapshotConf.Lock {
				dirPath = filepath.Join("/", snapshotConf.Volume, snapshotConf.DistDirName)
			}
		}
		if dirPath == data.MntPath { //如果文件夹被占用则返回true
			return true
		}
	}
	return false
}

// SnapshotOccupationFolder 查询快照占用文件夹
func (f *Folder) SnapshotOccupationFolder() []string {
	data := make([]string, 0)
	state := apputils.CheckAppState(snapshotName)
	if state == nil {
		return data
	}
	path := filepath.Join(state.InstallPath, "Snapshot", "cronTable", "duplicate")
	if !utils.Exists(path) {
		return data
	}
	dir, err := os.ReadDir(path)
	if err != nil {
		return data
	}

	for _, v := range dir {
		file, err := os.ReadFile(filepath.Join(path, v.Name()))
		if err != nil {
			continue
		}
		var snapshotConf SnapshotConf
		if err = json.Unmarshal(file, &snapshotConf); err != nil {
			continue
		}
		switch snapshotConf.Type {
		case "local":
			data = append(data, filepath.Join("/", snapshotConf.Volume, snapshotConf.DistDirName))
		case "remote":
			if snapshotConf.Lock {
				data = append(data, filepath.Join("/", snapshotConf.Volume, snapshotConf.DistDirName))
			}
		}
	}

	return data
}

func (f *Folder) clearNfsAndSmbAcl(id int) error {
	return f.dbService.DbGet().Where("folder_id=? and service in ?", id, []string{"samba", "nfs"}).Delete(&sqlitemodel.AclHost{}).Error
}

// ChangeVolume 参数example: /Volume1
func (f *Folder) ChangeVolume(newRoot string) error {
	if linux.GetDeviceInfoByPath(newRoot) == nil {
		return errors.New("invalid volume")
	}
	defaultAddr := linux.GetDefaultAddr()
	oldRoot := ""
	files, _ := filepath.Glob(filepath.Join("/Volume*/", ".main.inc"))
	for _, file := range files {
		if utils.GetFirstLine(file) == defaultAddr {
			oldRoot = filepath.Dir(file)
		} else {
			_ = os.Remove(file)
		}
	}
	_, _ = utils.WriteFile(filepath.Join(newRoot, ".main.inc"), defaultAddr)
	if oldRoot != newRoot {
		_ = f.ctlService.AppService(false)
		if oldRoot != "" {
			_ = f.moveHome(newRoot, oldRoot)
		}
	}
	_, _ = utils.ShellExec("/etc/init.d/nas/mkbase", newRoot)
	return nil
}

func (f *Folder) moveHome(newRoot, oldRoot string) error {
	newHomes := filepath.Join(newRoot, "User")
	if err := os.Mkdir(newHomes, 0777); err != nil {
		return gerror.Wrap(err, "moveHome")
	}
	if linux.GetDeviceInfo(oldRoot) != nil { //旧挂载点依然存在的情况下...
		oldUserHome := filepath.Join(oldRoot, "User")
		_ = fs.WalkDir(os.DirFS(oldUserHome), ".", func(path string, d fs.DirEntry, err error) error {
			if path == "." {
				return nil
			}
			//兼容拷贝目录或者文件
			_ = utils.ShellExecBackends("cp", filepath.Join(oldUserHome, d.Name()), newHomes+"/", "-a")
			if d.IsDir() {
				return fs.SkipDir
			}
			return nil
		})
	}
	return nil
}

func (f *Folder) GetWebDavAcl(folderID int) (result []sqlitemodel.AclWebdav) {
	webdavList := make([]sqlitemodel.AclWebdav, 0)
	db := f.dbService.DbGet()
	db.Table("acl_webdav").Where("folder_id=?", folderID).Find(&webdavList)
	users, _ := f.userService.UserList(0, 0)
	for _, userItem := range users {
		box := sqlitemodel.AclWebdav{Username: userItem.Name, FolderId: folderID}
		for _, item := range webdavList {
			if item.Username != box.Username {
				continue
			}
			box = item
		}
		result = append(result, box)
	}
	return
}

// GetSmbAclList 获取SmbAcl配置列表
func (f *Folder) GetSmbAclList(folderId int) ([]sharefolder.SmbAcl, error) {
	var res []sqlitemodel.AclHost
	_ = f.dbService.DbGet().Where("service=? and folder_id=?", "samba", folderId).Find(&res)
	data := make([]sharefolder.SmbAcl, 0)
	for _, v := range res {
		data = append(data, sharefolder.SmbAcl{
			FolderId:   v.FolderId,
			Hash:       v.Hash,
			Host:       v.Host,
			Permission: v.Access,
		})
	}
	return data, nil
}

// GetWebdavAclList 获取WebdavAcl配置列表
func (f *Folder) GetWebdavAclList(folderId int) ([]sharefolder.SmbAcl, error) {
	var res []sqlitemodel.AclHost
	_ = f.dbService.DbGet().Where("service=? and folder_id=?", "webdav", folderId).Find(&res)
	data := make([]sharefolder.SmbAcl, 0)
	for _, v := range res {
		data = append(data, sharefolder.SmbAcl{
			FolderId:   v.FolderId,
			Hash:       v.Hash,
			Host:       v.Host,
			Permission: v.Access,
		})
	}
	return data, nil
}

// UpdateSmbAcl 更新SmbAcl配置
func (f *Folder) UpdateSmbAcl(arg sharefolder.SmbAcl) error {
	//先判断被更新数据是否存在
	var num int64
	if f.dbService.DbGet().Model(&sqlitemodel.AclHost{}).Where("service=? and hash=?", "samba", arg.Hash).Count(&num); num == 0 {
		return errors.New("data does not exist")
	}
	num = 0
	//排除本身外是否还存在相同host
	if f.dbService.DbGet().Model(&sqlitemodel.AclHost{}).Where("service=? and folder_id=? and host=? and hash!=?", "samba", arg.FolderId, arg.Host, arg.Hash).Count(&num); num > 0 {
		return errors.New("host exist")
	}
	if err := f.DelSmbAcl(arg.Hash); err != nil {
		return gerror.Wrap(err, "UpdateNFSAcl1")
	}
	return f.SetSmbAcl(arg)
}

func (f *Folder) UpdateWebdavAcl(arg sharefolder.SmbAcl) error {
	//先判断被更新数据是否存在
	var num int64
	if f.dbService.DbGet().Model(&sqlitemodel.AclHost{}).Where("service=? and hash=?", "webdav", arg.Hash).Count(&num); num == 0 {
		return errors.New("data does not exist")
	}
	num = 0
	//排除本身外是否还存在相同host
	if f.dbService.DbGet().Model(&sqlitemodel.AclHost{}).Where("service=? and folder_id=? and host=? and hash!=?", "webdav", arg.FolderId, arg.Host, arg.Hash).Count(&num); num > 0 {
		return errors.New("host exist")
	}
	if err := f.DelWebdavAcl(arg.Hash); err != nil {
		return gerror.Wrap(err, "Update")
	}
	return f.SetWebDavAcl(arg)
}

// DelSmbAcl 删除SmbAcl配置
func (f *Folder) DelSmbAcl(hashString string) error {
	err := f.dbService.DbGet().Where("service=? and hash=?", "samba", hashString).Delete(sqlitemodel.AclHost{}).Error
	tosutils.ReStartBaseService("samba")
	return err
}

func (f *Folder) DelWebdavAcl(hashString string) error {
	err := f.dbService.DbGet().Where("service=? and hash=?", "webdav", hashString).Delete(sqlitemodel.AclHost{}).Error
	tosutils.ReStartBaseService("webdav")
	return err
}

// SetSmbAcl 设置SmbAcl配置
func (f *Folder) SetSmbAcl(arg sharefolder.SmbAcl) error {
	err := f.dbService.DbGet().Create(&sqlitemodel.AclHost{
		Host:     arg.Host,
		FolderId: arg.FolderId,
		Service:  "samba",
		Access:   arg.Permission,
		Hash:     utils.Gethashstring(fmt.Sprintf("%s%d%s", "samba", arg.FolderId, arg.Host)),
	}).Error
	tosutils.ReStartBaseService("samba")
	return err
}

// SetSmbAcl 设置SmbAcl配置
func (f *Folder) SetWebDavAcl(arg sharefolder.SmbAcl) error {
	err := f.dbService.DbGet().Create(&sqlitemodel.AclHost{
		Host:     arg.Host,
		FolderId: arg.FolderId,
		Service:  "webdav",
		Access:   arg.Permission,
		Hash:     utils.Gethashstring(fmt.Sprintf("%s%d%s", "webdav", arg.FolderId, arg.Host)),
	}).Error
	tosutils.ReStartBaseService("webdav")
	return err
}

// UpdateNFSAcl 更新NFSAcl配置
func (f *Folder) UpdateNFSAcl(arg sharefolder.NFSAcl) error {
	//先判断被更新数据是否存在
	var num int64
	if f.dbService.DbGet().Where("service=? and hash=?", "nfs", arg.Hash).First(&sqlitemodel.AclHost{}).Count(&num); num == 0 {
		return errors.New("data does not exist")
	}
	num = 0
	//排除本身外是否还存在相同host
	if f.dbService.DbGet().Model(&sqlitemodel.AclHost{}).Where("service=? and folder_id=? and host=? and hash!=?", "nfs", arg.FolderId, arg.Host, arg.Hash).Count(&num); num > 0 {
		return errors.New("host exist")
	}
	if err := f.DelNFSAcl(arg.Hash); err != nil {
		return gerror.Wrap(err, "UpdateNFSAcl1")
	}
	return f.SetNFSAcl(arg)
}

// DelNFSAcl 删除NFSAcl
func (f *Folder) DelNFSAcl(hashId string) error {
	err := f.dbService.DbGet().Where("service=? and hash=?", "nfs", hashId).Delete(sqlitemodel.AclHost{}).Error
	tosutils.ReStartBaseService("nfs")
	return err
}

// GetNFSAclList 获取NFSAcl列表
func (f *Folder) GetNFSAclList(folderId int) ([]sharefolder.NFSAcl, error) {
	data := make([]sharefolder.NFSAcl, 0)
	var conf []sqlitemodel.AclHost
	if err := f.dbService.DbGet().Where("service=? and folder_id=?", "nfs", folderId).Find(&conf).Error; err != nil {
		glog.Warning("GetNFSAclList", err)
		return data, nil
	}
	for _, v := range conf {
		nfsAcl := sharefolder.NFSAcl{}
		nfsAcl.Hash = v.Hash
		nfsAcl.Host = v.Host
		nfsAcl.FolderId = v.FolderId
		nfsAcl.Permission = v.Access
		config := make(map[string]*NfsOpts)
		if err := json.Unmarshal([]byte(v.Extend), &config); err != nil {
			glog.Warning("GetNFSAclList1", err)
			continue
		}
		//解析squash
		nfsAcl.Squash = 0
		//if config["squash"].Compare("no_root_squash") {
		uid := config["anonuid"].ToInt()

		if uid == 0 {
			if config["squash"].Compare("root_squash") {
				nfsAcl.Squash = 1
			}
			if config["squash"].Compare("all_squash") {
				nfsAcl.Squash = 3
			}
		} else {
			if config["squash"].Compare("root_squash") {
				nfsAcl.Squash = 2
			}
			if config["squash"].Compare("all_squash") {
				nfsAcl.Squash = 4
			}
		}

		if sec, ok := config["sec"]; ok {
			nfsAcl.Security = sec.ToString()
		}
		nfsAcl.PrivilegedPort = config["insecure"].Compare("insecure")
		nfsAcl.Async = config["async"].Compare("async")
		nfsAcl.IsMount = config["crossmnt"].Compare("crossmnt")
		data = append(data, nfsAcl)
	}
	return data, nil
}

// SetNFSAcl 设置nfs配置
func (f *Folder) SetNFSAcl(arg sharefolder.NFSAcl) error {
	uid, err := f.getIdFromName("guest")
	if err != nil {
		return gerror.Wrap(err, "SetNFSAcl")
	}
	if arg.Squash == 1 || arg.Squash == 3 { //admin -> super_user
		uid, err = f.getIdFromName(os.Getenv("admin"))
		if err != nil {
			return gerror.Wrap(err, "SetNFSAcl1")
		}
	}
	gid, _ := f.getIdFromName("@allusers")
	conf := make(map[string]*NfsOpts)
	conf["anonuid"] = &NfsOpts{Value: uid}
	conf["anonuid"].Combine("anonuid")
	conf["anongid"] = &NfsOpts{Value: gid}
	conf["anongid"].Combine("anongid")
	conf["squash"] = &NfsOpts{Value: "no_root_squash"}
	switch arg.Squash {
	case 1, 2:
		conf["squash"].ChangeVal("root_squash")
	case 3, 4:
		conf["squash"].ChangeVal("all_squash")
	}
	conf["squash"].Combine("")
	conf["sec"] = &NfsOpts{Value: arg.Security}
	conf["sec"].Combine("sec")
	conf["async"] = &NfsOpts{Value: ""}
	if arg.Async {
		conf["async"].ChangeVal("async")
	}
	conf["async"].Combine("")
	conf["crossmnt"] = &NfsOpts{Value: ""}
	if arg.IsMount {
		conf["crossmnt"].ChangeVal("crossmnt")
	}
	conf["crossmnt"].Combine("")
	conf["insecure"] = &NfsOpts{Value: ""}
	if arg.PrivilegedPort {
		conf["insecure"].ChangeVal("insecure")
	}
	conf["insecure"].Combine("")
	conf["insecure_locks"] = &NfsOpts{Value: "insecure_locks"}
	conf["insecure_locks"].Combine("")
	extend, err := json.Marshal(conf)
	if err != nil {
		return gerror.Wrap(err, "SetNFSAcl2")
	}
	if err = f.dbService.DbGet().Create(&sqlitemodel.AclHost{
		Host:     arg.Host,
		FolderId: arg.FolderId,
		Service:  "nfs",
		Access:   arg.Permission,
		Hash:     utils.Gethashstring(fmt.Sprintf("%s%d%s", "nfs", arg.FolderId, arg.Host)),
		Extend:   string(extend),
	}).Error; err != nil {
		return gerror.Wrap(err, "SetNFSAcl3")
	}
	//if _, err = utils.ShellExec("service nfs restart"); err != nil {
	//	return gerror.Wrap(err, "SetNFSAcl4")
	//}
	tosutils.ReStartBaseService("nfs")
	return nil
}

func (f *Folder) getIdFromName(name string) (id int, err error) {
	if strings.HasPrefix(name, "@") {
		var lookup *user.Group
		name = name[1:]
		lookup, err = user.LookupGroup(name)
		if err != nil {
			return
		}
		id, _ = strconv.Atoi(lookup.Gid)
		return
	}
	var lookup *user.User
	lookup, err = user.Lookup(name)
	if err != nil {
		return
	}
	id, _ = strconv.Atoi(lookup.Uid)
	return
}

// GetServiceAcl 获取nfs或者samba的安全策略
func (f *Folder) GetServiceAcl(id int, service string) (result sqlitemodel.AclHost, errResult error) {
	result = sqlitemodel.AclHost{}
	db := f.dbService.DbGet()
	tx := db.Table("acl_host").Where("folder_id=? and service=?", id, service)
	var total int64
	tx.Count(&total)
	if total > 0 {
		tx.Find(&result)
	} else {
		errResult = errors.New("no setting")
	}
	return
}

func (f *Folder) GetFolderNameById(id int) (string, error) {
	db := f.dbService.DbGet()
	tx := db.Table("share").Where("folder_id=?", id)
	var count int64
	tx.Count(&count)
	if count == 0 {
		return "", errors.New("invalid folder id")
	}
	result := sqlitemodel.Share{}
	tx.Find(&result)
	return result.MntPath, nil
}
func (f *Folder) isMounted(path string) bool {
	dfs, err := disk.Partitions(false)
	if err != nil {
		return false
	}
	for _, item := range dfs {
		if item.Mountpoint == path {
			return true
		}
	}
	return false
}

func (f *Folder) getMountPointByPath(path string) (device string) {
	mntPoint := filepath.Dir(path)
	dfs, err := disk.Partitions(false)
	if err != nil {
		return
	}
	for _, item := range dfs {
		if item.Mountpoint == mntPoint || item.Mountpoint == path {
			device = item.Device
			break
		}
	}
	return
}

// ObtainSecretKey 获取秘钥文件路径
func (f *Folder) ObtainSecretKey(path, pwd string) (*sharefolder.ObtainSecretKeyData, error) {
	dir, base := filepath.Split(path)
	path = filepath.Join(dir, strings.Trim(base, "@")) //去除首尾多余@符号
	res, e := utils.ShellExec(fmt.Sprintf(`df-json | grep '%s$' > /dev/null;echo $?`, path))
	if e != nil {
		return nil, gerror.Wrap(e, "ObtainSecretKey")
	}
	od := &sharefolder.ObtainSecretKeyData{}
	if strings.TrimSpace(res) != "0" {
		od.Code = 2
		return od, nil
	}

	path, status, e := f.dirService.ObtainSecretKey(path, pwd)
	if e != nil {
		return nil, gerror.Wrap(e, "ObtainSecretKey")
	}
	od.Src = utils.Base64Encode(path)
	od.Code = status
	return od, nil
}

// EscapePassword 密码的一些特殊字符要转义
func (f *Folder) EscapePassword(pwd string) string {
	var password []rune
	for _, v := range pwd {
		if unicode.IsPunct(v) || unicode.IsSymbol(v) || unicode.Is(unicode.Han, v) {
			password = append(password, '\\')
		}
		password = append(password, v)
	}
	return string(password)
}

// EcryptfsMount 加密文件夹挂载
func (f *Folder) EcryptfsMount(path, pwd string) (bool, error) {
	if e := f.dirService.EcryptfsMount(path, pwd); e != nil {
		return false, gerror.Wrap(e, "EcryptfsMount")
	}
	// start-bug fix: ID1010174 重装系统后被挂载的加密文件夹内有 #recycle 这个文件夹，但并未开启回收站功能
	dir, base := filepath.Split(path)

	path = filepath.Join(dir, strings.Trim(base, "@")) //去除首尾多余@符号
	var enableRecycle int
	// 根据有无 #recycle 这个文件夹更新数据库相应的值
	if utils.Exists(filepath.Join(path, "#recycle")) {
		enableRecycle = 1
	}
	db := f.dbService.DbGet()
	db.Model(sqlitemodel.Share{}).Where("mntpath=?", path).Update("recycle", enableRecycle)
	// end-bug fix: ID1010174 重装系统后被挂载的加密文件夹内有 #recycle 这个文件夹，但并未开启回收站功能

	tosutils.ReStartBaseService("")
	return true, nil
}

// EcryptfsUmount 加密文件夹卸载
func (f *Folder) EcryptfsUmount(path string) (bool, error) {
	if e := f.dirService.EcryptfsUmount(path); e != nil {
		return false, gerror.Wrap(e, "EcryptfsUmount")
	}
	tosutils.ReStartBaseService("")
	return true, nil
}

// GetRichAclUserList 获取acl用户列表
func (f *Folder) GetRichAclUserList(path string) acl.RichAclUser {
	//设置成richAcl模式
	old := f.aclService.SetNextOptAclType(acl.RichAcl)
	//改回之前的模式
	defer f.aclService.SetNextOptAclType(old)

	args := strings.Split(path, "&")

	from := "fileShare"
	if len(args) >= 2 {
		path = args[0]
		from = args[1]
	}

	users := acl.RichAclUser{}
	users.Users = make([]string, 0)
	users.Groups = make([]string, 0)

	cnt := 0
	if path[len(path)-1] == '/' {
		cnt = 1
	}

	cnt = strings.Count(path, "/") - cnt
	if cnt <= 2 && from != "fileManagement" {
		userList, _ := f.userService.UserList(0, 0)
		for _, userItem := range userList {
			users.Users = append(users.Users, userItem.Name)
		}
		groupList, _ := f.userService.GroupList(0, 0)
		for _, groupItem := range groupList {
			users.Groups = append(users.Groups, groupItem.Name)
		}
		return users
	}

	fid, ok := f.aclService.GetAclTypeByFile(path)
	if ok {
		for user, aclType := range fid {
			if aclType != int(acl.RichAcl) {
				continue
			}

			if len(user) > 0 && user[0] == '@' {
				users.Groups = append(users.Groups, user[1:])
			} else {
				users.Users = append(users.Users, user)
			}
		}
	}
	return users
}

func (f *Folder) EditAcl(b int, path string) error {
	var acls []string
	ucmd := fmt.Sprintf(`getfacl '%s' | grep -E "^user:"`, path)
	uList, err := utils.ShellExec(ucmd)
	if err != nil {
		return gerror.Wrap(err, "recycleStatus user")
	}
	for _, v := range strings.Split(uList, "\n") {
		parts := strings.Split(v, ":")
		if len(parts) < 3 || parts[1] == "" {
			continue
		}
		//过滤admin组的用户
		inGroup, _ := userInGroup(parts[1], "admin")
		if inGroup {
			continue
		}
		role := "rwx"
		if b == 2 {
			role = "-wx"
		}
		acls = append(acls, fmt.Sprintf(`-m u:%s:%s '%s'`, parts[1], role, path))
	}

	gcmd := fmt.Sprintf(`getfacl '%s' | grep -E "^group:"`, path)
	gList, err := utils.ShellExec(gcmd)
	if err != nil {
		return gerror.Wrap(err, "recycleStatus group")
	}
	for _, v := range strings.Split(gList, "\n") {
		parts := strings.Split(v, ":")
		if len(parts) < 3 || parts[1] == "" {
			continue
		}
		//过滤admin组
		if parts[1] == "admin" {
			continue
		}
		role := "rwx"
		if b == 2 {
			role = "-wx"
		}
		acls = append(acls, fmt.Sprintf(`-m g:%s:%s '%s'`, parts[1], role, path))
	}

	if b == 2 {
		acls = append(acls, fmt.Sprintf(`-m g:admin:rwx '%s'`, path))
	}
	//批量设置用户规则
	aclStr := fmt.Sprintf(`setfacl  %s`, strings.Join(acls, " "))
	if _, err = utils.ShellExec(aclStr); err != nil {
		return gerror.Wrap(err, "set acl failed")
	}

	return nil
}

func userInGroup(username, groupname string) (bool, error) {
	// 执行命令获取用户所属的用户组列表
	cmd := exec.Command("groups", username)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return false, err
	}

	// 将输出转换为字符串，并检查目标用户组是否在列表中
	groups := string(output)
	if strings.Contains(groups, groupname) {
		return true, nil
	}
	return false, nil
}
